<?php
namespace App\Http\Controllers\Payment;

use App\Http\Controllers\Controller;
use App\Jobs\ClosePaymentJob;
use App\Jobs\PushMsgJob;
use App\Jobs\SendSystemMsgJob;
use App\Jobs\ShopTransJob;
use App\Jobs\WechatRefundJob;
use App\Models\HongBao\HongBao;
use App\Models\Shop\Shop;
use App\Models\Shop\ShopCoupon;
use App\Models\Shop\ShopCouponPurchase;
use App\Models\Shop\ShopManager;
use Carbon\Carbon;
use EasyWeChat\Foundation\Application;
use EasyWeChat\Payment\Order as wechat_order;
use App\Libraries\Tools\ConstVar;
use App\Libraries\Tools\Msg;
use App\Libraries\Tools\Tools;
use App\Models\Order\Order;
use App\Models\Post\Post;
use App\Models\Post\PostPurchase;
use App\Models\Product\Product;
use App\Models\Product\ProductPurchase;
use App\Models\User\Address;
use App\Models\User\Balance;
use App\Models\User\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;


class WeChatPayController extends Controller{

    protected $app_id;
    protected $merchant_id;
    protected $key;
    protected $cert_path;
    protected $key_path;
    protected $option;
    protected $notify_url = 'http://api.beboy.me/api/payment/wechat/notify';
    protected $local_notify_url = 'http://api.beboy.me:8081/api/payment/wechat/notify';
	protected $post;
	protected $order;
	protected $postPurchase;
	protected $auth_user;
	protected $address;
	protected $product;
	protected $productPurchase;
	protected $user;
	protected $balance;
	protected $shopCoupon;
	protected $shopCouponPurchase;
    protected $hongBao;

    public function __construct(Order $order, Post $post, User $user,
								ShopCoupon $shopCoupon, ShopCouponPurchase $shopCouponPurchase,
                                PostPurchase $postPurchase, Address $address,
                                Product $product, ProductPurchase $productPurchase,
								Balance $balance, HongBao $hongBao){
        $this->order = $order;
        $this->post = $post;
        $this->postPurchase = $postPurchase;
        $this->auth_user = Auth::user();
        $this->address = $address;
        $this->product = $product;
        $this->productPurchase = $productPurchase;
        $this->user = $user;
        $this->balance = $balance;
        $this->shopCoupon = $shopCoupon;
        $this->shopCouponPurchase = $shopCouponPurchase;
        $this->hongBao = $hongBao;

        $this->app_id = env('WECHAT_APPID');
        $this->merchant_id = env('WECHAT_PAYMENT_MERCHANT_ID');
        $this->key = env('WECHAT_PAYMENT_KEY');
        $this->cert_path = env('WECHAT_PAYMENT_CERT_PATH');
        $this->key_path = env('WECHAT_PAYMENT_KEY_PATH');

        if(env('APP_ENV') == 'local'){
            $this->notify_url = $this->local_notify_url;
        }

        $this->option = [
            'app_id' => $this->app_id,
            'payment' => [
                'merchant_id'        => $this->merchant_id,
                'key'                => $this->key,
                'cert_path'          => $this->cert_path,
                'key_path'           => $this->key_path,
                'notify_url'         => $this->notify_url,
                'device_info'        => 'WEB',
            ],
        ];
    }

    /**
     * 统一下单
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function preOrder(Request $request){
        $validator = Validator::make($request->all(), [
            'id' => 'required|integer',
            'type' => 'required|in:1,2,3,4,5,6',//1-post 2-product 3-transfer_to_shop 4-coupon 5-transfer_to_user 6-hongbao
            'address_id' => 'integer',
            'quantity' => 'integer',
            'hong_bao_id' => 'integer',
            'transfer_amount' => 'numeric|required_if:type,3,5,6',
            'app_type' => 'in:1,2',//1-app 2-小程序
            'code' => 'required_if:app_type,2',
        ], [
            'id.required' => '10001',
            'id.integer' => '10002',
            'type.required' => '10001',
            'type.in' => '10002',
            'address_id.integer' => '10002',
            'quantity.integer' => '10002',
            'hong_bao_id.integer' => '10002',
            'transfer_amount.numeric' => '10002',
            'transfer_amount.required_if' => '10001',
            'app_type.in' => '10002',
            'code.required_if' => '10001',
        ]);

        if ($validator->fails()) {
            return $this->sendFailResponse(400, $validator->errors()->first());
        }

        switch($request['type']){//1-post 2-product 3-transfer_to_shop 4-coupon 5-transfer_to_user 6-hongbao
            case ConstVar::$ORDER_TYPE_POST:
                $model = $this->post;
                $model_ext = $this->postPurchase;
                break;
            case ConstVar::$ORDER_TYPE_PRODUCT:
                $model = $this->product;
                $model_ext = $this->address;
                break;
            case ConstVar::$ORDER_TYPE_TRANSFER_TO_SHOP:
                $model = new User();
                $model_ext = new Shop();
                break;
            case ConstVar::$ORDER_TYPE_COUPON:
                $model = $this->shopCoupon;
                $model_ext = $this->hongBao;
                break;
            case ConstVar::$ORDER_TYPE_TRANSFER_TO_USER:
                $model = new User();
                $model_ext = null;
                break;
            default:
                return $this->sendFailResponse(400, 50001);
        }

        $rs = $this->order->logic($request, $this->auth_user, ConstVar::$ORDER_CHANNEL_WECHAT, $model, $model_ext);
        if($rs['code'] != 200){
            return $this->sendFailResponse(400, $rs['code']);
        }

        $job = (new ClosePaymentJob($rs['data']['out_trade_no']))->delay(Carbon::now()->addMinutes(ConstVar::$PAYMENT_CLOSE_DELAY));
        dispatch($job);

        $real_amount = $this->order->total_amount - $this->order->hong_bao_amount;

        $attributes = [
            'trade_type'       => 'APP', // JSAPI，NATIVE，APP...
            'body'             => $rs['data']['subject'],
            'detail'           => $rs['data']['body'],
            'out_trade_no'     => $rs['data']['out_trade_no'],
            'total_fee'        => $real_amount * 100, // 单位：分
        ];

        switch($request['app_type']){
            case 2:
                $mini = (new Application([
                    'mini_program' => [
                        'app_id' => env('MINIPROGRAM_APPID'),
                        'secret' => env('MINIPROGRAM_APPSECRET'),
                    ],
                ]))->mini_program->sns->getSessionKey($request['code']);
                $this->app_id = env('MINIPROGRAM_APPID');
                $this->option['app_id'] = $this->app_id;
                $attributes['trade_type'] = 'JSAPI';
                $attributes['openid'] = $mini->openid;
                break;
        }

        $app = new Application($this->option);
        $payment = $app->payment;
        $wechat_order = new wechat_order($attributes);
        $result = $payment->prepare($wechat_order);
        $time = time();
        $attributes = [
            'appid' => $this->app_id,
            'partnerid' => $this->merchant_id,
            'prepayid' => $result->prepay_id,
            'package' => 'Sign=WXPay',
            'noncestr' => uniqid(),
            'timestamp' => $time
        ];
        switch($request['app_type']){
            case 2:
                $attributes = [
                    'appId' => $this->app_id,
                    'package' => 'prepay_id='.$result->prepay_id,
                    'nonceStr' => uniqid(),
                    'signType' => 'MD5',
                    'timeStamp' => $time
                ];
                break;
        }
        $sign = \EasyWeChat\Payment\generate_sign($attributes, $this->key);
        if ($result->return_code == 'SUCCESS' && $result->result_code == 'SUCCESS'){
            $response = [
                'app_id' => $this->app_id,
                'merchant_id' => $this->merchant_id,
                'prepay_id' => $result->prepay_id,
                'nonce_str' => $attributes['noncestr']??$attributes['nonceStr'],
                'timestamp' => $time,
                'sign' => $sign,
            ];
            Cache::put($rs['data']['out_trade_no'], \GuzzleHttp\json_encode($response), Carbon::now()->addMinutes(15));
            return $this->sendSuccessResponse($response);
        }
        return $this->sendFailResponse(500, 50000);
    }

    /**
     * 回调
     * @return \Symfony\Component\HttpFoundation\Response
     * @throws \EasyWeChat\Core\Exceptions\FaultException
     */
    public function notify(){
        $app = new Application($this->option);
        $response = $app->payment->handleNotify(function($notify, $successful){
            DB::beginTransaction();
            try{
                $order_info = explode('_', $notify->out_trade_no);
                $order = $this->order->where('id', $order_info[0])->firstOrFail();
                if($successful){
                    if(empty($order->status)){//0-待支付 1-成功 2-失败 3-退款
                        $order->status = ConstVar::$ORDER_STATUS_SUCCESS;
                    }else{
                        return true;
                    }
                    $order->sid = $notify->transaction_id;
                    $order->buyer_channel_id = $notify->openid;
                    $order->buyer_channel_account = $notify->openid;
                    $order->receipt_amount = $notify->total_fee/100;

                    switch($order_info[1]){//1-post 2-product 3-transfer
                        case ConstVar::$ORDER_TYPE_POST:
                            $postPurchase = $this->postPurchase->where('order_id',$order_info[0])->first();
                            $postPurchase->status=1;
                            $postPurchase->save();
                            $purchase_id = $postPurchase->id;
                            $this->balance->add(
                                Msg::wechat_consumption().'_'.Msg::balance_post_profit($order_info[2]),
                                $order->user_id,
                                $order_info[3],
                                ConstVar::$BALANCE_CHANNEL_WECHAT,
                                ConstVar::$BALANCE_CHANNEL_BALANCE,
                                ConstVar::$ORDER_TYPE_POST,
                                $order_info[2],
                                $order->total_amount,
                                $purchase_id
                            );
//                            $this->user->where('user_id', $order_info[3])->increment('balance', $order->total_amount);
                            break;
                        case ConstVar::$ORDER_TYPE_PRODUCT:
                            $productPurchase = $this->productPurchase->where('order_id',$order_info[0])->first();
                            $productPurchase->status=1;
                            $productPurchase->save();
                            $purchase_id = $productPurchase->id;
                            $this->balance->add(//消费记录
                                Msg::balance_consumption().'_'.Msg::balance_product_profit($order_info[2]),
                                $order->user_id,
                                0,
                                ConstVar::$BALANCE_CHANNEL_WECHAT,
                                ConstVar::$BALANCE_CHANNEL_BALANCE,
                                ConstVar::$ORDER_TYPE_PRODUCT,
                                $order_info[2],
                                $order->receipt_amount,
                                $purchase_id,
                                $order->hong_bao_id,
                                $order->hong_bao_amount,
                                ConstVar::$BALANCE_STATUS_SUCCESS,
                                ConstVar::$BALANCE_TYPE_USER,
                                ConstVar::$BALANCE_TYPE_SYSTEM
                            );
                            $product = Product::find($productPurchase->product_id);
                            if($product->category == 2){
//                              todo      dispatch(new MLMJob($user_id, $channel, $product_id, $this->id));
                                (new User)->addVIP($order->user_id, $product->level, $product->validity);
                            }
                            break;
                        case ConstVar::$ORDER_TYPE_TRANSFER_TO_SHOP:
                            Shop::where('shop_id', $order_info[2])->increment('balance', $order->total_amount);
                            $owner = ShopManager::where('shop_id', $order_info[2])
                                ->where('status', 1)->where('level', 1)->first();
                            $this->balance->add(//transfer记录
                                Msg::wechat_consumption().'_'.Msg::balance_transfer_profit(),
                                $order->user_id,
                                $order_info[2],
                                ConstVar::$BALANCE_CHANNEL_WECHAT,
                                ConstVar::$BALANCE_CHANNEL_BALANCE,
                                ConstVar::$ORDER_TYPE_TRANSFER_TO_SHOP,
                                $order_info[2],
                                $order->total_amount,
                                0,
                                $order->hong_bao_id,
                                $order->hong_bao_amount,
                                ConstVar::$BALANCE_STATUS_SUCCESS,
                                ConstVar::$BALANCE_TYPE_USER,
                                ConstVar::$BALANCE_TYPE_SHOP
                            );
                            if(!empty($owner)){
                                dispatch(new SendSystemMsgJob([$owner->user_id], Msg::transfer_to_shop($order->total_amount), 0));
                                dispatch(new PushMsgJob([$owner->user_id], Msg::transfer_to_shop($order->total_amount), 0));
                                dispatch(new ShopTransJob($order->id, $owner->user_id, $order_info[2]));
                            }
                            break;
						case ConstVar::$ORDER_TYPE_COUPON:
                            $shopCouponPurchase = $this->shopCouponPurchase->where('order_id',$order_info[0])->first();
                            $shopCouponPurchase->status=0;
                            $shopCouponPurchase->save();
                            $purchase_id = $shopCouponPurchase->id;
                            $this->balance->add(//消费记录
                                Msg::wechat_consumption().'_'.Msg::balance_coupon_profit($order_info[2]),
                                $order->user_id,//from_id
                                0,
                                ConstVar::$BALANCE_CHANNEL_WECHAT,
                                ConstVar::$BALANCE_CHANNEL_BALANCE,
                                ConstVar::$ORDER_TYPE_COUPON,
                                $order_info[2],//coupon_id
                                $order->receipt_amount,
                                $purchase_id,
                                $order->hong_bao_id,
                                $order->hong_bao_amount,
                                ConstVar::$BALANCE_STATUS_SUCCESS,
                                ConstVar::$BALANCE_TYPE_USER,
                                ConstVar::$BALANCE_TYPE_SYSTEM
                            );
							break;
                        case ConstVar::$ORDER_TYPE_TRANSFER_TO_USER:
                            User::where('user_id', $order_info[2])->increment('balance', $order->total_amount);
                            $this->balance->add(//transfer记录
                                Msg::wechat_consumption().'_'.Msg::balance_transfer_profit(),
                                $order->user_id,
                                $order_info[2],
                                ConstVar::$BALANCE_CHANNEL_WECHAT,
                                ConstVar::$BALANCE_CHANNEL_BALANCE,
                                ConstVar::$ORDER_TYPE_TRANSFER_TO_USER,
                                $order_info[2],
                                $order->total_amount,
                                0,
                                $order->hong_bao_id,
                                $order->hong_bao_amount,
                                ConstVar::$BALANCE_STATUS_SUCCESS,
                                ConstVar::$BALANCE_TYPE_USER,
                                ConstVar::$BALANCE_TYPE_USER
                            );
                            break;
                        case ConstVar::$ORDER_TYPE_HONGBAO:
                            DB::rollBack();
                            return 'fail';
                            break;
                        default:
                            DB::rollBack();
                            return 'fail';
                            break;
                    }
                }else{
                    $order->status = ConstVar::$ORDER_STATUS_FAIL;
                }
                $order->save();
                DB::commit();
            }catch (\Exception $e){
                DB::rollBack();
                Log::critical('wechatpay fail', $_POST);
                Log::critical('wechatpay fail', ['msg'=>$e->getTraceAsString()]);
                return 'fail';
            }
            return true; // 或者错误消息
        });
        return $response;
    }

    public function refund(Request $request){
        $validator = Validator::make($request->all(), [
            'id' => 'required|integer',
            'type' => 'required|in:1,2,3,4,5,6',//1-post 2-product 3-transfer_to_shop 4-coupon 5-transfer_to_user 6-hongbao
        ], [
            'id.required' => '10001',
            'id.integer' => '10002',
            'type.required' => '10001',
            'type.in' => '10002',
        ]);

        if ($validator->fails()) {
            return $this->sendFailResponse(400, $validator->errors()->first());
        }

        switch($request['type']){//1-post 2-product 3-transfer 4-coupon
            case 1:
                return $this->sendFailResponse(400, 50001);
            case 2:
                return $this->sendFailResponse(400, 50001);
            case 3:
                //todo
                return $this->sendFailResponse(400, 50001);
            case 4:
                $shopCouponPurchase = $this->shopCouponPurchase->where('id', $request['id'])->first();
                if(empty($shopCouponPurchase) || empty($shopCouponPurchase->order_id)){
                    return $this->sendFailResponse(400, 40400);
                }
                if($shopCouponPurchase->status != ConstVar::$PURCHASE_STATUS_UNUSED){
                    return $this->sendFailResponse(400, 40001);
                }

                $order = $this->order->where('id', $shopCouponPurchase->order_id)->first();
                if(empty($order)){
                    return $this->sendFailResponse(400, 40400);
                }
                if($order->status != ConstVar::$ORDER_STATUS_SUCCESS){
                    return $this->sendFailResponse(400, 40001);
                }
                if($order->user_id != Auth::id() || $shopCouponPurchase->user_id != Auth::id()){
                    return $this->sendFailResponse(400, 40001);
                }
                $shopCouponPurchase->status = ConstVar::$PURCHASE_STATUS_REFUNDING;
                $order->status = ConstVar::$ORDER_STATUS_REFUNDING;
                $shopCouponPurchase->save();
                $order->save();
                $this->balance->where('purchase_id', $shopCouponPurchase->id)
                    ->where('from_id', $shopCouponPurchase->user_id)->update(['status'=>2]);
                $this->balance->add(//消费记录
                    Msg::system_refund().'_'.Msg::system_refund(),
                    0,//from_id
                    $shopCouponPurchase->user_id,
                    ConstVar::$BALANCE_CHANNEL_BALANCE,
                    ConstVar::$BALANCE_CHANNEL_WECHAT,
                    ConstVar::$ORDER_TYPE_COUPON,
                    $shopCouponPurchase->coupon_id,//coupon_id
                    $order->receipt_amount,
                    $shopCouponPurchase->id,
                    $order->hong_bao_id,
                    $order->hong_bao_amount,
                    ConstVar::$BALANCE_STATUS_SUCCESS,
                    ConstVar::$BALANCE_TYPE_SYSTEM,
                    ConstVar::$BALANCE_TYPE_USER,
                    ConstVar::$BALANCE_REFUND
                );
                break;
            default:
                return $this->sendFailResponse(400, 50001);
        }
        $app = new Application($this->option);
        $payment = $app->payment;
        dispatch((new WechatRefundJob($payment, $order, $shopCouponPurchase, Auth::id()))->delay(Carbon::now()->addMinutes(ConstVar::$PAYMENT_REFUND_DELAY)));
        return $this->sendSuccessResponse([]);
    }

//    public function trans(){
//        $app = new Application($this->option);
//        $merchantPay = $app->merchant_pay;
//        $merchantPayData = [
//            'partner_trade_no' => str_random(16), //随机字符串作为订单号，跟红包和支付一个概念。
//            'openid' => 'oWc-kwRVaVwyKyqllF0Q3MRcRHJ8', //收款人的openid
//            'check_name' => 'NO_CHECK',  //文档中有三种校验实名的方法 NO_CHECK OPTION_CHECK FORCE_CHECK
//            're_user_name'=>'古维',     //OPTION_CHECK FORCE_CHECK 校验实名的时候必须提交
//            'amount' => 100,  //单位为分
//            'desc' => '企业付款',
//            'spbill_create_ip' => '119.29.1.227',  //发起交易的IP地址
//        ];
//        $result = $merchantPay->send($merchantPayData);
//        Log::debug(json_encode($result));
//        return $this->sendSuccessResponse($result);
//    }
}